<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\access;

use Hoa\Exception\Exception;
use lflow\access\dialect\Dialect;
use lflow\access\dialect\MySqlDialect;
use lflow\DBAccess;
use lflow\entity\CCOrder;
use lflow\entity\HistoryOrder;
use lflow\entity\HistoryTask;
use lflow\entity\Order;
use lflow\entity\Process;
use lflow\entity\Surrogate;
use lflow\entity\Task;
use lflow\entity\TaskActor;
use lflow\helper\StringHelper;
use stdClass;

/**
 * 数据库操作抽象类
 *
 * @author Mr.April
 * @since  1.0
 */
abstract class AbstractDBAccess implements DBAccess
{
    protected static string $KEY_SQL = "SQL";
    protected static string $KEY_ARGS = "ARGS";
    protected static string $KEY_ENTITY = "ENTITY";
    protected static string $KEY_SU = "SU";
    protected static string $SAVE = "SAVE";

    protected static string $UPDATE = "UPDATE";

    /**
     * Sql语句封装
     */
    use Sql;

    /**
     * saveOrUpdate
     * isORM为true，则参数map只存放对象
     * isORM为false，则参数map需要放入SQL、ARGS、TYPE
     *
     * @param object|string $map
     */
    public abstract function saveOrUpdate(object $map);

    /**
     * buildMap  需要构造map传递给实现类
     *
     * @param string|object $param
     * @param array|string  $args
     *
     * @return object|string
     */
    private function buildMap(string|object $param, array|string $args): object|string
    {
        if (is_object($param)) {
            $map = [
                self::$KEY_ENTITY => $param,
                self::$KEY_SU     => $args,
            ];
        }
        if (!is_object($param)) {
            $map = [
                self::$KEY_SQL  => $param,
                self::$KEY_ARGS => $args,
            ];
        }
        return (object)$map;
    }

    protected function getDialect(): Dialect
    {
        return new MySqlDialect();
    }

    /**
     * saveProcess
     *
     * @param \lflow\entity\Process $process
     *
     * @throws \lflow\WorkFlowException
     */
    public function saveProcess(Process $process): void
    {
        $args = [
            $process->getId(),
            $process->getName(),
            $process->getDisplayName(),
            $process->getType(),
            $process->getInstanceUrl(),
            $process->getState(),
            $process->getVersion(),
            $process->getCreateTime(),
            $process->getCreator(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$PROCESS_INSERT, $args));
    }

    public function updateProcess(Process $process): void
    {
        $args = [
            $process->getName(),
            $process->getType(),
            $process->getDisplayName(),
            $process->getState(),
            $process->getInstanceUrl(),
            $process->getCreateTime(),
            $process->getCreator(),
            $process->getId(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$PROCESS_UPDATE, $args));
    }

    public function deleteProcess(Process $process): void
    {
        $args = [$process->getId()];
        $this->saveOrUpdate($this->buildMap(self::$PROCESS_DELETE, $args));
    }

    public function updateProcessType(string $id, string $type): void
    {
        $args = [$type, $id];
        $this->saveOrUpdate($this->buildMap(self::$PROCESS_UPDATE_TYPE, $args));
    }

    public function saveTask(Task $task): void
    {
        $args = [
            $task->getId(),
            $task->getOrderId(),
            $task->getTaskName(),
            $task->getDisplayName(),
            $task->getTaskType(),
            $task->getPerformType(),
            $task->getOperator(),
            $task->getCreateTime(),
            $task->getFinishTime(),
            $task->getExpireTime(),
            $task->getActionUrl(),
            $task->getParentTaskId(),
            !empty($task->getVariable()) ? $task->getVariable() : json_encode(new stdClass()),
            $task->getVersion(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$TASK_INSERT, $args));
    }

    public function saveOrder(Order $order): void
    {
        $args = [
            $order->getId(),
            $order->getProcessId(),
            $order->getCreator(),
            $order->getCreateTime(),
            $order->getParentId(),
            $order->getParentNodeName(),
            $order->getExpireTime(),
            $order->getLastUpdateTime(),
            $order->getLastUpdator(),
            $order->getOrderNo(),
            !empty($order->getVariable()) ? $order->getVariable() : json_encode(new stdClass()),
            $order->getVersion(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$ORDER_INSERT, $args));
    }

    public function saveCCOrder(CCOrder $ccorder): void
    {
        $args = [
            $ccorder->getOrderId(),
            $ccorder->getActorId(),
            $ccorder->getCreator(),
            $ccorder->getCreateTime(),
            $ccorder->getStatus(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$CCORDER_INSERT, $args));
    }

    /**
     * @param \lflow\entity\TaskActor $taskActor
     */
    public function saveTaskActor(TaskActor $taskActor): void
    {
        $args = [$taskActor->getTaskId(), $taskActor->getActorId()];
        $this->saveOrUpdate($this->buildMap(self::$TASK_ACTOR_INSERT, $args));
    }

    public function updateTask(Task $task): void
    {
        $args = [
            $task->getFinishTime(),
            $task->getOperator(),
            !empty($task->getVariable()) ? $task->getVariable() : json_encode(new stdClass()),
            $task->getExpireTime(),
            $task->getActionUrl(),
            $task->getId(),
            $task->getVersion(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$TASK_UPDATE, $args));
    }

    public function updateOrder(Order $order): void
    {
        $args = [
            $order->getLastUpdator(),
            $order->getLastUpdateTime(),
            !empty($order->getVariable()) ? $order->getVariable() : json_encode(new stdClass()),
            $order->getExpireTime(),
            $order->getId(),
            $order->getVersion(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$ORDER_UPDATE, $args));
    }

    public function updateCCOrder(CCOrder $ccorder): void
    {
        $args = [
            $ccorder->getStatus(),
            $ccorder->getFinishTime(),
            $ccorder->getOrderId(),
            $ccorder->getActorId(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$CCORDER_UPDATE, $args));
    }

    public function deleteTask(Task $task): void
    {
        $args = [$task->getId()];
        $this->saveOrUpdate($this->buildMap(self::$TASK_ACTOR_DELETE, $args));
        $this->saveOrUpdate($this->buildMap(self::$TASK_DELETE, $args));
    }

    /**
     * deleteOrder
     *
     * @param \lflow\entity\Order $order
     */
    public function deleteOrder(Order $order): void
    {
        $this->saveOrUpdate($this->buildMap(self::$ORDER_DELETE, [$order->getId()]));
    }

    public function deleteCCOrder(CCOrder $ccorder): void
    {
        $this->saveOrUpdate($this->buildMap(self::$CCORDER_DELETE, [$ccorder->getActorId()]));
    }

    public function removeTaskActor(string $taskId, string $actors): void

    {
        foreach (explode(',', $actors) as $actorsId) {
            $this->saveOrUpdate($this->buildMap(self::$TASK_ACTOR_REDUCE, [$taskId, $actorsId]));
        }
    }

    public function saveHistory(HistoryOrder|HistoryTask $entity): void
    {
        if ($entity instanceof HistoryOrder) {
            $args = [
                $entity->getId(),
                $entity->getProcessId(),
                $entity->getOrderState(),
                $entity->getCreator(),
                $entity->getCreateTime(),
                $entity->getEndTime(),
                $entity->getParentId(),
                $entity->getExpireTime(),
                $entity->getOrderNo(),
                !empty($entity->getVariable()) ? $entity->getVariable() : json_encode(new stdClass()),
            ];
            $this->saveOrUpdate($this->buildMap(self::$ORDER_HISTORY_INSERT, $args));
        }
        if ($entity instanceof HistoryTask) {
            $args = [
                $entity->getId(),
                $entity->getOrderId(),
                $entity->getTaskName(),
                $entity->getDisplayName(),
                $entity->getTaskType(),
                $entity->getPerformType(),
                $entity->getTaskState(),
                $entity->getOperator(),
                $entity->getCreateTime(),
                $entity->getFinishTime(),
                $entity->getExpireTime(),
                $entity->getActionUrl(),
                $entity->getParentTaskId(),
                !empty($entity->getVariable()) ? $entity->getVariable() : json_encode(new stdClass()),
            ];
            $this->saveOrUpdate($this->buildMap(self::$TASK_HISTORY_INSERT, $args));
            if (!empty($entity->getActorIds())) {

                foreach ($entity->getActorIds() as $actorId) {
                    if (empty($actorId)) {
                        continue;
                    }
                    $this->saveOrUpdate($this->buildMap(self::$TASK_ACTOR_HISTORY_INSERT, [$entity->getId(), $actorId]));
                }
            }
        }
    }

    public function updateHistory(HistoryOrder $order): void
    {
        $args = [
            $order->getOrderState(),
            $order->getEndTime(),
            !empty($order->getVariable()) ? $order->getVariable() : json_encode(new stdClass()),
            $order->getId(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$ORDER_HISTORY_UPDATE, $args));
    }

    public function deleteHistoryOrder(HistoryOrder $historyOrder): void
    {
        $args = [$historyOrder->getId()];
        $this->saveOrUpdate($this->buildMap(self::$ORDER_HISTORY_DELETE, $args));
    }

    public function deleteHistoryTask(HistoryTask $historyTask): void
    {
        $args = [$historyTask->getId()];
        $this->saveOrUpdate($this->buildMap(self::$TASK_ACTOR_HISTORY_DELETE, $args));
        $this->saveOrUpdate($this->buildMap(self::$TASK_HISTORY_DELETE, $args));
    }

    public function updateOrderVariable(Order $order): void
    {
        $this->updateOrder($order);
        $hist = $this->getHistOrder($order->getId());
        $hist->setVariable($order->getVariable());
        $this->updateHistory($hist);
    }

    public function saveSurrogate(Surrogate $surrogate): void
    {
        $args = [
            $surrogate->getId(),
            $surrogate->getProcessName(),
            $surrogate->getOperator(),
            $surrogate->getSurrogate(),
            $surrogate->getOdate(),
            $surrogate->getSdate(),
            $surrogate->getEdate(),
            $surrogate->getState(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$SURROGATE_INSERT, $args));
    }

    public function updateSurrogate(Surrogate $surrogate): void
    {
        $args = [
            $surrogate->getProcessName(),
            $surrogate->getSurrogate(),
            $surrogate->getOdate(),
            $surrogate->getSdate(),
            $surrogate->getEdate(),
            $surrogate->getState(),
            $surrogate->getId(),
        ];
        $this->saveOrUpdate($this->buildMap(self::$SURROGATE_UPDATE, $args));
    }

    public function deleteSurrogate(Surrogate $surrogate): void
    {
        $args = [$surrogate->getId()];
        $this->saveOrUpdate($this->buildMap(self::$SURROGATE_DELETE, $args));
    }

    public function getSurrogate(string $id): ?Surrogate
    {
        $where = " where id = ?";
        return $this->queryObject(Surrogate::class, self::$SURROGATE_QUERY . $where, [$id]);
    }

    public function getSurrogateList(QueryFilter $filter, Page $page = null): array
    {
        $sql       = self::$SURROGATE_QUERY;
        $sql       .= " where 1=1 and state = 1 ";
        $paramList = [];
        if (!empty($filter->getNames())) {
            $sql .= " and process_name in(";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getOperators())) {
            $sql .= " and operator in (";
            foreach (explode(',', $filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getOperateTime())) {
            $sql         .= " and sdate <= ? and edate >= ? ";
            $paramList[] = $filter->getOperateTime();
            $paramList[] = $filter->getOperateTime();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::DESC);
            $filter->setOrderBy("sdate");
        }
        return $this->queryResult('', $filter, $sql, $paramList);
    }

    public function getTask(string $taskId): ?Task
    {
        $where = " where id = ?";
        return $this->queryObject(Task::class, self::$QUERY_TASK . $where, [$taskId]);
    }

    public function getNextActiveTasks(string $parentTaskId): array
    {
        $where = "where parent_task_id = ?";
        return $this->queryList(Task::class, self::$QUERY_TASK . $where, [$parentTaskId]);
    }

    public function getNextActiveTasksX(string $orderId, string $taskName, string $parentTaskId): array
    {
        $sql  = self::$QUERY_TASK . " where parent_task_id in ( select ht.id from wf_hist_task ht where ht.order_id=? and ht.task_name=? and ht.parent_task_id=? )";
        $args = [$orderId, $taskName, $parentTaskId];
        return $this->queryList(Task::class, $sql, $args);
    }

    public function getHistTask(string $taskId): ?HistoryTask
    {
        $where = " where id = ?";
        return $this->queryObject(HistoryTask::class, self::$QUERY_HIST_TASK . $where, [$taskId]);
    }

    public function getHistOrder(string $orderId): ?HistoryOrder
    {
        $where = " where id = ?";
        return $this->queryObject(HistoryOrder::class, self::$QUERY_HIST_ORDER . $where, [$orderId]);
    }

    public function getTaskActorsByTaskId(string $taskId): array
    {
        $where = " where task_id = ?";
        return $this->queryList('', self::$QUERY_TASK_ACTOR . $where, [$taskId]);
    }

    public function getHistTaskActorsByTaskId(string $taskId): array
    {
        $where = " where task_Id = ?";
        return $this->queryList('', self::$QUERY_HIST_TASK_ACTOR . $where, [$taskId]);
    }

    public function getOrder(string $orderId): ?Order
    {
        $where = " where 1 = 1 and id = ?";
        return $this->queryObject(Order::class, self::$QUERY_ORDER . $where, [$orderId]);
    }

    public function getCCOrder(string $orderId, string $actorId = ''): array
    {
        $where = " where 1 = 1 ";
        $aras  = [];
        if (!empty($orderId)) {
            $aras[] = $orderId;
            $where  .= " and order_Id = ?";
        }
        if (!empty($actorId)) {
            $where .= " and actor_id in (";
            $where .= str_repeat("?,", count(explode(',', $actorId)));
            $where = substr($where, 0, strlen($where) - 1);
            $where .= ") ";
            $aras  = array_merge($aras, explode(',', $actorId));
        }
        return $this->queryList('', self::$QUERY_CCORDER . $where, $aras);
    }

    public function getProcess(string $id): ?Process
    {
        $where = " where id = ?";
        return $this->queryObject(Process::class, self::$QUERY_PROCESS . $where, [$id]);
    }

    public function getProcesss(QueryFilter $filter, Page $page = null): array
    {
        $sql       = self::$QUERY_PROCESS . '  where 1=1 ';
        $paramList = [];
        if (!empty($filter->getNames())) {
            $sql .= " and name in(";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getVersion())) {
            $sql         .= " and version = ? ";
            $paramList[] = $filter->getVersion();
        }
        if (!empty($filter->getState())) {
            $sql         .= " and state = ? ";
            $paramList[] = $filter->getState();
        }
        if (!empty($filter->getDisplayName())) {
            $sql         .= " and display_Name like ? ";
            $paramList[] = "%" . $filter->getDisplayName() . "%";
        }
        if (!empty($filter->getProcessType())) {
            $sql         .= " and type = ? ";
            $paramList[] = $filter->getProcessType();
        }
        if (!empty($filter->getOperators())) {
            $sql .= " and creator in(";
            foreach (explode(',', $filter->getNgetOperatorsames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::ASC);
            $filter->setOrderBy("name");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getActiveOrders(QueryFilter $filter, Page $page = null): array
    {
        $sql       = self::$QUERY_ORDER;
        $sql       .= " left join wf_process p on p.id = o.process_id ";
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOperators())) {
            $sql .= " and creator in(";
            foreach (explode(',', $filter->getNgetOperatorsames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getNames())) {
            $sql .= " and creator in(";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getProcessId())) {
            $sql         .= " and o.process_id = ? ";
            $paramList[] = $filter->getProcessId();
        }
        if (!empty($filter->getDisplayName())) {
            $sql         .= " and p.display_name like ? ";
            $paramList[] = $filter->getDisplayName();
        }
        if (!empty($filter->getProcessType())) {
            $sql         .= " and p.type = ? ";
            $paramList[] = $filter->getProcessType();
        }
        if (!empty($filter->getParentId())) {
            $sql         .= " and o.parent_id = ? ";
            $paramList[] = $filter->getParentId();
        }
        if (!empty($filter->getExcludedIds())) {
            $sql .= " and o.id not in(";
            foreach (explode(',', $filter->getExcludedIds()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and o.create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and o.create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!empty($filter->getOrderNo())) {
            $sql         .= " and o.order_no = ? ";
            $paramList[] = $filter->getOrderNo();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::ASC);
            $filter->setOrderBy("o.create_time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getActiveTasks(QueryFilter $filter, Page $page = null): array
    {
        $sql          = self::$QUERY_TASK;
        $isFetchActor = !empty($filter->getOperators()) ?? false;
        if ($isFetchActor) {
            $sql .= " left join wf_task_actor ta on ta.task_id = id ";
        }
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOrderId())) {
            $sql         .= " and order_id = ? ";
            $paramList[] = $filter->getOrderId();
        }
        if (!empty($filter->getExcludedIds())) {
            $sql .= " and id not in(";
            foreach (explode(',', $filter->getExcludedIds()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if ($isFetchActor) {
            $sql .= " and ta.actor_id in (";
            foreach (explode(',', $filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getNames())) {
            $sql .= " and task_name in (";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::ASC);
            $filter->setOrderBy("create_time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getHistoryOrders(QueryFilter $filter, Page $page = null): array
    {
        $sql       = self::$QUERY_HIST_ORDERX;
        $sql       .= " left join wf_process p on p.id = o.process_id ";
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOperators())) {
            $sql .= " and o.creator in(";
            foreach (explode(',', $filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getNames())) {
            $sql .= " and p.name in(";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getProcessId())) {
            $sql         .= " and o.process_id = ? ";
            $paramList[] = $filter->getProcessId();
        }
        if (!empty($filter->getProcessType())) {
            $sql         .= " and p.type = ? ";
            $paramList[] = $filter->getProcessType();
        }
        if (!empty($filter->getDisplayName())) {
            $sql         .= " and p.display_name like ? ";
            $paramList[] = '%' . $filter->getDisplayName() . '%';
        }
        if (!empty($filter->getParentId())) {
            $sql         .= " and o.parent_Id = ? ";
            $paramList[] = $filter->getParentId();
        }
        if (!empty($filter->getState())) {
            $sql         .= " and o.order_state = ? ";
            $paramList[] = $filter->getState();
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and o.create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and o.create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!empty($filter->getOrderNo())) {
            $sql         .= " and o.order_no = ? ";
            $paramList[] = $filter->getOrderNo();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::DESC);
            $filter->setOrderBy("o.create_time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getHistoryTasks(QueryFilter $filter, Page $page = null): array
    {
        $sql          = self::$QUERY_HIST_TASK;
        $isFetchActor = !empty($filter->getOperators()) ?? false;
        if ($isFetchActor) {
            $sql .= " left join wf_hist_task_actor ta on ta.task_id = id ";
        }
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOrderId())) {
            $sql         .= " and order_Id = ? ";
            $paramList[] = $filter->getOrderId();
        }
        if ($isFetchActor) {
            $sql .= " and ta.actor_id in (";
            foreach (explode(',', $filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getNames())) {
            $sql .= " and task_name in (";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::DESC);
            $filter->setOrderBy("finish_time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getWorkItems(QueryFilter $filter, Page $page = null): array
    {
        $sql       = " select distinct o.process_id, t.order_id, t.id as id, t.id as task_id, p.display_name as process_name, p.instance_url, o.parent_id, o.creator, ";
        $sql       .= " o.create_time as order_create_time, o.expire_time as order_expire_time, o.order_no, o.variable as order_variable, ";
        $sql       .= " t.display_name as task_name, t.task_name as task_key, t.task_type, t.perform_type, t.operator, t.action_url, ";
        $sql       .= " t.create_time as task_create_time, t.finish_time as task_end_time, t.expire_time as task_expire_time, t.variable as task_variable ";
        $sql       .= " from wf_task t ";
        $sql       .= " left join wf_order o on t.order_id = o.id ";
        $sql       .= " left join wf_task_actor ta on ta.task_id=t.id ";
        $sql       .= " left join wf_process p on p.id = o.process_id ";
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOperators())) {
            $sql .= " and ta.actor_id in (";
            foreach (explode(',', (string)$filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getProcessId())) {
            $sql         .= " and o.process_id = ?";
            $paramList[] = $filter->getProcessId();
        }
        if (!empty($filter->getDisplayName())) {
            $sql         .= " and p.display_name like ?";
            $paramList[] = $filter->getDisplayName();
        }
        if (!empty($filter->getParentId())) {
            $sql         .= " and o.parent_id = ? ";
            $paramList[] = $filter->getParentId();
        }
        if (!empty($filter->getOrderId())) {
            $sql         .= " and t.order_id = ? ";
            $paramList[] = $filter->getOrderId();
        }
        if (!empty($filter->getNames())) {
            $sql .= " and t.task_name in (";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getTaskType())) {
            $sql         .= " and t.task_type = ? ";
            $paramList[] = $filter->getTaskType();
        }
        if (!empty($filter->getPerformType())) {
            $sql         .= " and t.perform_type = ? ";
            $paramList[] = $filter->getPerformType();
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and t.create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and t.create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::DESC);
            $filter->setOrderBy("t.create_time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getCCWorks(QueryFilter $filter, Page $page = null): array
    {
        $sql       = " select id,process_id,order_state,priority,cc.creator,cc.create_time,end_time,parent_id,expire_time,order_no,variable ";
        $sql       .= " from wf_cc_order cc ";
        $sql       .= " left join wf_hist_order o on cc.order_id = o.id ";
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOperators())) {
            $sql .= " and cc.actor_id in(";
            foreach (explode(',', $filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getState())) {
            $sql         .= " and cc.status = ? ";
            $paramList[] = $filter->getState();
        }
        if (!empty($filter->getProcessId())) {
            $sql         .= " and process_id = ? ";
            $paramList[] = $filter->getProcessId();
        }
        if (!empty($filter->getParentId())) {
            $sql         .= " and parent_id = ? ";
            $paramList[] = $filter->getParentId();
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and cc.create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and cc.create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!empty($filter->getOrderNo())) {
            $sql         .= " and order_no = ? ";
            $paramList[] = $filter->getOrderNo();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::DESC);
            $filter->setOrderBy("cc.create_Time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function getHistoryWorkItems(QueryFilter $filter, Page $page = null): array
    {
        $sql       = " select distinct o.process_id, t.order_id, t.id as id, t.id as task_id, p.display_name as process_name, p.instance_url, o.parent_id, o.creator, ";
        $sql       .= " o.create_time as order_create_time, o.expire_time as order_expire_time, o.order_no, o.variable as order_variable, ";
        $sql       .= " t.display_name as task_name, t.task_name as task_key, t.task_type, t.perform_type,t.operator, t.action_url, ";
        $sql       .= " t.create_time as task_create_time, t.finish_time as task_end_time, t.expire_time as task_expire_time, t.variable as task_variable ";
        $sql       .= " from wf_hist_task t ";
        $sql       .= " left join wf_hist_order o on t.order_id = o.id ";
        $sql       .= " left join wf_hist_task_actor ta on ta.task_id=t.id ";
        $sql       .= " left join wf_process p on p.id = o.process_id ";
        $sql       .= " where 1=1 ";
        $paramList = [];
        if (!empty($filter->getOperators())) {
            $sql .= " and ta.actor_id in (";
            foreach (explode(',', (string)$filter->getOperators()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getProcessId())) {
            $sql         .= " and o.process_id = ?";
            $paramList[] = $filter->getProcessId();
        }
        if (!empty($filter->getDisplayName())) {
            $sql         .= " and p.display_name like ?";
            $paramList[] = $filter->getDisplayName();
        }
        if (!empty($filter->getParentId())) {
            $sql         .= " and o.parent_id = ? ";
            $paramList[] = $filter->getParentId();
        }
        if (!empty($filter->getOrderId())) {
            $sql         .= " and t.order_id = ? ";
            $paramList[] = $filter->getOrderId();
        }
        if (!empty($filter->getNames())) {
            $sql .= " and t.task_name in (";
            foreach (explode(',', $filter->getNames()) as $value) {
                $sql         .= "?,";
                $paramList[] = $value;
            }
            $sql = substr($sql, 0, strlen($sql) - 1);
            $sql .= ") ";
        }
        if (!empty($filter->getTaskType())) {
            $sql         .= " and t.task_type = ? ";
            $paramList[] = $filter->getTaskType();
        }
        if (!empty($filter->getPerformType())) {
            $sql         .= " and t.perform_Type = ? ";
            $paramList[] = $filter->getPerformType();
        }
        if (!empty($filter->getCreateTimeStart())) {
            $sql         .= " and t.create_time >= ? ";
            $paramList[] = $filter->getCreateTimeStart();
        }
        if (!empty($filter->getCreateTimeEnd())) {
            $sql         .= " and t.create_time <= ? ";
            $paramList[] = $filter->getCreateTimeEnd();
        }
        if (!$filter->isOrderBySetted()) {
            $filter->setOrder(QueryFilter::DESC);
            $filter->setOrderBy("t.create_time");
        }
        return $this->queryResult('', $filter, $sql, $paramList, $page);
    }

    public function queryResult(string $class, QueryFilter $filter, string $sql, array $args, Page|null $page = null): array
    {
        $orderby  = StringHelper::buildPageOrder($filter->getOrder(), $filter->getOrderBy());
        $querySQL = $sql . $orderby;
        if (empty($page)) {
            return $this->queryList($class, $querySQL, $args);
        }
        //追加分页sql
        $querySQL = $this->getDialect()->getPageSql($querySQL, $page);
        try {
            $count = $this->queryCount($sql, $args);
            $list  = $this->queryList('', $querySQL, $args);
            $page->setResult($list);
            $page->setTotalCount((int)$count);
            return $page->toArray();
        } catch (Exception $e) {
            return ['pageIndex' => 1, 'pageSize' => 15, 'total' => 0, 'list' => []];
        }
    }

    /**
     * 分页查询时，符合条件的总记录数
     *
     * @param string $sql
     * @param object $args
     *
     * @return object
     */
    protected abstract function queryCount(string $sql, object $args): int;

}